/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
 * Description: UDK log src file
 * Author: -
 * Create: 2021.4.23
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

#include "securec.h"
#include "udk_common.h"
#include "udk_cycles.h"
#include "udk_log.h"

/* for rsyslog */
#define UDK_LOG_TAG "LibLogTag_UDK"
#define MAX_LOG_TAG_LEN 32
#define MAX_LOG_PRE_LEN 32

/* The udk_log structure. */
struct udk_logs {
    uint32_t level; /* Log level. */
    size_t types_len;
    uint32_t sub_mod_level[UDK_LOGTYPE_FIRST_EXT_ID];
    char log_tag[MAX_LOG_TAG_LEN];
    char log_prefix[MAX_LOG_PRE_LEN];
};

static struct udk_logs g_udk_logs = {
    .level = UDK_LOG_INFO,
    .types_len = 0,
    .sub_mod_level = { 0 },
    .log_tag = UDK_LOG_TAG,
    .log_prefix = "",
};

void udk_log_set_global_level(uint32_t level)
{
    g_udk_logs.level = level;
}

void udk_log_set_log_prefix(void)
{
    int ret;
    FILE *cfg_fp = NULL;

    /* read log_conf */
    cfg_fp = fopen("/etc/hinic/log.conf", "r");
    if (cfg_fp == NULL) {
        return;
    }
    
    ret = fscanf_s(cfg_fp, "log_prefix:%s", g_udk_logs.log_prefix, MAX_LOG_PRE_LEN - 1);
    if (ret != 1) {
        memset_s(g_udk_logs.log_prefix, MAX_LOG_PRE_LEN, 0, MAX_LOG_PRE_LEN);
    }

    (void)fclose(cfg_fp);
}

int udk_log_set_level(uint32_t id, uint32_t level)
{
    if (id >= g_udk_logs.types_len || level > UDK_LOG_DEBUG) {
        return -EINVAL;
    }

    g_udk_logs.sub_mod_level[id] = level;
    return 0;
}

int udk_vlog(const char *function, int line, uint32_t level, uint32_t id, const char *format, va_list ap)
{
    int ret;
    char newformat[MAX_LOG_LEN + 1] = { 0 };
    char logmsg[MAX_LOG_LEN + 1] = { 0 };

    if (level > g_udk_logs.level) {
        return 0;
    }

    if (id >= g_udk_logs.types_len) {
        return -EINVAL;
    }

    if (level > g_udk_logs.sub_mod_level[id]) {
        return 0;
    }

#ifdef UDK_LOG_DEBUG_ON
    ret = snprintf_s(newformat, sizeof(newformat), sizeof(newformat) - 1, "%s%s|%s:[%d]|%s",
        g_udk_logs.log_prefix, g_udk_logs.log_tag, function, line, format);
#else
    UDK_REF_VAR(function);
    UDK_REF_VAR(line);
    ret = snprintf_s(newformat, sizeof(newformat), sizeof(newformat) - 1, "%s%s|%s",
        g_udk_logs.log_prefix, g_udk_logs.log_tag, format);
#endif
    if (ret <= 0) {
        return ret;
    }

    ret = vsnprintf_s(logmsg, sizeof(logmsg), sizeof(newformat) - 1, newformat, ap);
    if (ret <= 0) {
        return ret;
    }

    syslog((int)level, "%s", logmsg);
    return ret;
}

int udk_log(const char *function, int line, uint32_t level, uint32_t logtype, const char *format, ...)
{
    va_list ap;
    int ret;

    va_start(ap, format);
    ret = udk_vlog(function, line, level, logtype, format, ap);
    va_end(ap);
    return ret;
}

void udk_log_init(const char *log_tag)
{
    uint32_t i;
    int ret;

    (void)setlogmask(LOG_UPTO(LOG_DEBUG));

    /* register legacy log types */
    for (i = 0; i < UDK_LOGTYPE_FIRST_EXT_ID; i++) {
        g_udk_logs.sub_mod_level[i] = UDK_LOG_INFO;
    }

    g_udk_logs.types_len = UDK_LOGTYPE_FIRST_EXT_ID;

    if (log_tag != NULL) {
        ret = snprintf_s(g_udk_logs.log_tag, MAX_LOG_TAG_LEN, MAX_LOG_TAG_LEN - 1, "%s", log_tag);
        if (ret < 0) {
            udk_log(__func__, __LINE__, UDK_LOG_WARNING, UDK_LOGTYPE_COMMON, "New log tag %s set failed", log_tag);
            (void)snprintf_s(g_udk_logs.log_tag, MAX_LOG_TAG_LEN, MAX_LOG_TAG_LEN - 1, "%s", UDK_LOG_TAG);
            return;
        }
    }
}

static pthread_mutex_t ratelimit_mutex = PTHREAD_MUTEX_INITIALIZER;

/*
 * This enforces a log rate limit: not more than 20 messages
 * every 1s to make a denial-of-service attack impossible.
 */
DEFINE_RATELIMIT_STATE(udk_log_ratelimit_state, 1, 20);

static int ratelimit_judge(struct log_ratelimit_state *rs, const char *func)
{
    int ret;

    if (!rs->interval)
        return 1;

    pthread_mutex_lock(&ratelimit_mutex);

    if (rs->unit) {
        rs->interval = rs->interval * udk_get_timer_hz();
        rs->unit = 0;
    }

    if (!rs->begin) {
        rs->begin = udk_get_timer_cycles();
    }

    if (rs->begin + rs->interval < udk_get_timer_cycles()) {
        if (rs->missed) {
            UDK_LOG(NOTICE, COMMON, "%s: %d callbacks suppressed\n", func, rs->missed);
        }
        rs->begin = 0;
        rs->printed = 0;
        rs->missed = 0;
    }

    if (rs->burst && rs->burst > rs->printed) {
        rs->printed++;
        ret = 1;
    } else {
        rs->missed++;
        ret = 0;
    }
    pthread_mutex_unlock(&ratelimit_mutex);

    return ret;
}

int ratelimit(const char *func)
{
    return ratelimit_judge(&udk_log_ratelimit_state, func);
}

int udk_set_ratelimit_para(uint64_t interval, int burst)
{
    struct log_ratelimit_state *rs = &udk_log_ratelimit_state;

    if (interval == 0 || burst == 0) {
        UDK_LOG(NOTICE, COMMON, "Set udk log rate limit failed, invalid para, interval:%lu, burst:%d.\n", rs->interval,
            rs->burst);
        return -EINVAL;
    }

    pthread_mutex_lock(&ratelimit_mutex);

    rs->interval = interval;
    rs->burst = burst;

    UDK_LOG(NOTICE, COMMON, "Set udk log rate limit succeed, interval:%lu, burst:%d.\n", rs->interval, rs->burst);
    pthread_mutex_unlock(&ratelimit_mutex);

    return 0;
}
